package com.kalvan.log.util;

import com.kalvan.sensitive.annotation.Desensitized;
import com.kalvan.sensitive.utils.SensitiveInfoUtils;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 日志格式化工具，各种java对象转换为标准格式日志，才能被日志框架给自动脱敏
 *
 * @author chenliang
 */
public class LogFormatUtil {

    public static final String JAVA_LANG_OBJECT = "java.lang.Object";
    public static final String JAVA_LANG = "java.lang";
    public static final String JAVA_UTIL = "java.util";
    public static final String JAVA_MATH = "java.math";

    /**
     * java对象输出一行日志 key:value <br>
     * 如果属性里面有List 或者 Map 不打印<br>
     * 如果需要打印后面扩展方法参数来实现递归打印或者不打印<br>
     *
     * @param object 对象
     * @return 格式化日志
     */
    public static String formatBean(Object object) {
        return formatBean(object, false, null);
    }

    public static String formatBean(Object object, boolean beautiful, String splitSpace) {
        try {
            if (object == null) {
                return "Object is NULL.";
            } else {
                return isBaseClass(object.getClass()) ? object.toString() : getPropertyStringToLine(object.getClass(), object, beautiful, splitSpace);
            }
        } catch (Exception var2) {
            return "Read object properties error.";
        }
    }

    /**
     * 输出map内部日志 key:value 慎重使用，可能有性能问题
     *
     * @param map map
     * @return 格式化日志
     */
    public static String formatMap(Map<?, ?> map) {
        StringBuilder sb = new StringBuilder();
        if (map == null) {
            return "Map is NULL.";
        }
        for (Map.Entry<?, ?> mapSingle : map.entrySet()) {
            sb.append(mapSingle.getKey());
            sb.append(":");
            if (mapSingle.getValue() instanceof String) {
                sb.append(mapSingle.getValue());
            } else {
                sb.append(formatBean(mapSingle.getValue()));
            }
            sb.append(" ");
        }
        return sb.toString();
    }

    public static String formatMap(Map<?, ?> map, boolean beautiful, String splitSpace) {
        StringBuilder sb = new StringBuilder();
        if (map == null) {
            return "Map is NULL.";
        }
        for (Map.Entry<?, ?> mapSingle : map.entrySet()) {
            sb.append(mapSingle.getKey());
            sb.append(":");
            if (mapSingle.getValue() instanceof String) {
                sb.append(mapSingle.getValue());
            } else {
                sb.append(formatBean(mapSingle.getValue(), beautiful, splitSpace));
            }
            sb.append(" ");
        }
        return sb.toString();
    }

    /**
     * 输出集合内容日志 key:value 慎重使用，可能有性能问题
     *
     * @param collection 集合
     * @return 格式化日志
     */
    public static String formatCollection(Collection<?> collection) {
        if (collection == null) {
            return "Object is NULL.";
        }
        StringBuilder s = new StringBuilder();
        for (Iterator<?> iterator = collection.iterator(); iterator.hasNext(); ) {
            Object object = iterator.next();
            s.append(formatBean(object)).append(" ");
        }
        return s.toString();
    }

    /**
     * 输出集合内容日志 key:value 慎重使用，可能有性能问题
     *
     * @param collection 集合
     * @param beautiful  美化输出
     * @param splitSpace 分隔符
     * @return 格式化日志
     */
    public static String formatCollection(Collection<?> collection, boolean beautiful, String splitSpace) {
        if (collection == null) {
            return "Object is NULL.";
        }
        StringBuilder s = new StringBuilder();
        for (Iterator<?> iterator = collection.iterator(); iterator.hasNext(); ) {
            Object object = iterator.next();
            s.append(formatBean(object, beautiful, splitSpace)).append(" ");
        }
        return s.toString();
    }

    /**
     * 输出Object[]内容日志 key:value 慎重使用，可能有性能问题
     *
     * @param objectArray 数组
     * @return 格式化日志
     */
    public static String formatObjectArray(Object[] objectArray) {
        if (objectArray == null) {
            return "Object[] is NULL.";
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < objectArray.length; i++) {
            // 为了解决hessian调用Icheck的日志打印问题
            if (objectArray[i] instanceof Collection) {
                sb.append(formatCollection((Collection<?>) objectArray[i]));
                sb.append(" ");
                // 暂无使用
            } else if (objectArray[i] instanceof Map) {
                sb.append(formatMap((Map<?, ?>) objectArray[i]));
                sb.append(" ");
            } else {
                sb.append(formatBean(objectArray[i]));
                sb.append(" ");
            }

        }
        return sb.toString();
    }

    /**
     * 拼接对象属性和值
     *
     * @param c
     * @param entityName
     * @param splitSpace
     * @return
     * @throws Exception
     */
    private static String getPropertyStringToLine(Class<?> c, Object entityName, boolean beautiful, String splitSpace)
            throws Exception {
        String className = c.getSimpleName();
        Field[] field = c.getDeclaredFields();
        StringBuilder sb = new StringBuilder();
        if (splitSpace == null) {
            splitSpace = "";
        }
        if (beautiful) {
            sb.append(splitSpace);
            sb.append(className);
            sb.append("{");
        } else {
            sb.append(className);
            sb.append("{");
        }
        for (Field f : field) {
            f.setAccessible(true);
            if ("serialVersionUID".equals(f.getName())) {
                //不输出serialVersionUID
                continue;
            }
            if (isBaseClass(f.getType())) {
                if (f.getType().isAssignableFrom(List.class)) {
                    Collection cl = (Collection) f.get(entityName);
                    if (cl != null && cl.size() <= 10) {
                        //属性如果是List 同时是小数据量则打印出来
                        //空值不输出,值固定缩进"  "
                        sb.append(splitSpace);
                        sb.append("  ");
                        sb.append(f.getName());
                        sb.append(":");
                        sb.append(formatCollection(cl, true, splitSpace + "    "));
                        continue;
                    }
                }
                String valueStr = StringUtils.trimToEmpty(f.get(entityName) == null ? null
                        : f.get(entityName).toString());
                //脱敏日志输出
                Desensitized annotation = f.getAnnotation(Desensitized.class);
                if (annotation != null) {
                    valueStr = SensitiveInfoUtils.convertMsg(annotation.type(), valueStr);
                }
                if (StringUtils.isNotBlank(valueStr)) {
                    if (beautiful) {
                        //空值不输出,值固定缩进"  "
                        sb.append(splitSpace);
                        sb.append("  ");
                        sb.append(f.getName());
                        sb.append(":");
                        sb.append(valueStr);
                    } else {
                        sb.append(f.getName());
                        sb.append(":");
                        sb.append(valueStr);
                        sb.append(" ");
                    }
                }
            } else {
                String name = f.getName();
                String firstLetter = name.substring(0, 1).toUpperCase();
                String getMethodName = "get" + firstLetter + name.substring(1);
                Method getMethod = c.getMethod(getMethodName);
                Object value = getMethod.invoke(entityName);
                if (value != null) {
                    if (beautiful) {
                        //递归查到属性,子属性缩进"  "
                        sb.append(getPropertyStringToLine(value.getClass(), value, beautiful, splitSpace + "  "));
                    } else {
                        sb.append(getPropertyStringToLine(value.getClass(), value, beautiful, splitSpace + "  "));
                    }
                }
            }
        }
        sb.append(splitSpace).append("}");
        if (c.getSuperclass() == null
                || c.getSuperclass() == java.io.Serializable.class
                || c.getSuperclass() == java.lang.Object.class) {
            return sb.toString();
        }
        sb.append(getPropertyStringToLine(c.getSuperclass(), entityName, beautiful, splitSpace));
        return sb.toString();
    }

    /**
     * 判断Java类型
     *
     * @param c 类
     * @return true/false
     */
    public static boolean isBaseClass(Class<?> c) {
        if (c.isPrimitive()) {
            //基础数据类型
            return true;
        }
        if (JAVA_LANG_OBJECT.equals(c.getName())) {
            //可能有范形的参数对象,也进行遍历输出参数
            return false;
        }
        return JAVA_LANG.equals(c.getPackage().getName())
                || JAVA_UTIL.equals(c.getPackage().getName())
                || JAVA_MATH.equals(c.getPackage().getName());
    }
}
